// Cliente: Cliente de java que se comunica con los servidores (ARDUINOS)

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;
import java.util.TimeZone;
import java.util.Vector;
import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory;  
import org.apache.log4j.*; 
//import javax.mail.*;
//import javax.mail.internet.*;
//import javax.activation.*;

import operaciones.Operaciones;

public class Cliente extends Thread {
	
	// VER FUNCIONAMIENTO DE ESTA VERSIÓN EN LA SIGUIENTE DIRECCIÓN:
	// https://sites.google.com/site/jesfmm/project-updates/


	static Vector<Cliente> clientes = new Vector<Cliente>();
	static int base;
	static int numerodeservidores;

	static String establecerfecha = "SET;7;";;
	static String peticiontemperaturayhumedad = "GET;5" + '\n';
	static String peticiontemperatura = "GET;6" + '\n';
	static boolean modosilencioso;

	private ObjectOutputStream salida;
	private ObjectInputStream entrada;
	private String mensaje = "";
	private String servidor = "localhost";
	private Socket cliente = null;
	private int port;
	private boolean modotest;
	Thread hiloLectura;
	Thread hiloEscritura;
	Random r;

	private String stringSalida = null;
	boolean conexionActiva;
	boolean peticionDesconexion;
	int contador;

	Operaciones operacion;
	
	//logs
	//private final static Logger log = Logger.getLogger("Cliente");
    
	private final static Log smtp = LogFactory.getLog("Cliente");  
	
	
	// Parámetros:
	// Tiempo entre peticiones de temperatura y humedad (en minutos)
	static int t1=1;
	// Tiempo entre peticiones de establecer fecha en servidores (en horas)
	static int t2=24;
	// Localización de la base de datos:
	static String sURL = "jdbc:mysql://localhost:3306/HORUS2";
	static String user = "root";
	static String pass = "jesus";
	// Email alertas:
	static String email="jesusfmarin@gmail.com";
	
	public Cliente(int puerto) {

		port = puerto;
		hiloLectura = hiloLectura();
		hiloEscritura = hiloEscritura();

		this.r = new Random();
		r.setSeed(new Date().getTime());
		//System.out.println(port);
		//System.out.println("Constructor del cliente para el servidor " + port);

		peticionDesconexion = false;
		conexionActiva = true;

		contador = 0;

		operacion = new Operaciones(port,sURL,user,pass);
		
		modotest=false;
		
		//modosilencioso=false;
	}

	public Cliente() {
	}

	private void ejecutarCliente() {

		// while(conexionActiva){

		// Conectarse al servidor, obtener flujos de datos, y procesar la
		// conexión
		try {
			conectarAServidor(); // Paso 1: crear un socket para realizar la
									// conexión
			System.out.println("Conectado a servidor " + port);
			obtenerFlujos(); // Paso 2: obtener los flujos de datos entrada y
								// salida
			System.out.println("Flujos de datos obtenidos con el servidor "
					+ port);
			procesarConexion(); // Paso 3: procesar la conexión

		}

		// El servidor cerró la conexión
		catch (EOFException excepcionEOF) {
			System.err
					.println("El cliente termino la conexion con el servidor "
							+ port);
		}

		// Procesar los problemas que pueden ocurrir al comunicarse con el
		// servidor
		catch (IOException excepcionES) {
			excepcionES.printStackTrace();
		}

		finally {

			//System.out.println("Excepcion en -finally- de ejecutarCliente");
			// cerrarConexion(); // Paso 4: cerrar la conexión
		}

		// procesar problemas con E/S
		// catch (IOException excepcionES) {
		// excepcionES.printStackTrace();
			//System.out.println("Conexion cerrada");
		// }

	} 
	// Fin del método ejecutarCliente

	// Conectarse al servidor
	private void conectarAServidor() throws IOException {

		// if(conexionActiva){
		// crear Socket para realizar la conexión con el servidor
		while (cliente == null) {
			try {
				//System.out.println("Intentando conectar al servidor");
				cliente = new Socket(InetAddress.getByName(servidor), port);

			} catch (IOException excepcionES) {
				//System.out.println("No se puede conectar a servidor " + port);
				try {
					sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		}
		System.out.println("Conexión " + " recibida de: "
				+ cliente.getInetAddress().getHostName());
		//log.info("Conexion con servidor "+port);
		
		smtp.info("Conexion con servidor "+port);
		// }
	}

	// Obtener flujos para enviar y recibir datos
	private void obtenerFlujos() throws IOException {

		// if(conexionActiva){
		// Establecer flujo de salida para los objetos
		//System.out.println("Intentando obtener el flujo de datos de salida");
		salida = new ObjectOutputStream(cliente.getOutputStream());
		salida.flush(); // vacíar búfer de salida para enviar información de encabezado
		//System.out.println("Flujo de datos de salida obtenido");
		//System.out.println("Intentando obtener el flujo de datos de entrada");
		
		// Establecer flujo de entrada para los objetos
		entrada = new ObjectInputStream(cliente.getInputStream());
		//System.out.println("Flujo de datos de entrada obtenido");
		System.out.println("Flujos de datos obtenidos con servidor "+port);
		// }

	}

	// Procesar la conexión con el servidor
	private void procesarConexion() throws IOException {

		// System.out.println(Thread.threadStatus);
		hiloLectura.start();
		hiloEscritura.start();
		System.out.println("Hilos de lectura y escritura comenzados con servidor "+port);
		do {
			// procearConexion en cliente
			// System.out.println("Procesamiento de la conexión");

			try {
				sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		} while (conexionActiva);
		// while (!mensaje.equals("[ARDUINO] TERMINAR"));
		//System.out.println("Fin de procesarConexion");

	} 
	// Fin del método procesarConexion

	// Cerrar flujos y socket
	private void cerrarConexion() {
		System.out.println("Cerrando conexion con el servidor " + port);
		//log.info("Desconexion con servidor "+port);
		//smtp.info("Desconexion con servidor "+port);

		try {
			salida.close();
			entrada.close();
			cliente.close();
			cliente = null;
			servidor = null;
			conexionActiva = false;

		} catch (IOException excepcionES) {
			excepcionES.printStackTrace();
		}

		System.out.println("Conexion con servidor " + port + " cerrada");
		// try {
		// sleep(5000);
		// } catch (InterruptedException e) {
		// TODO Auto-generated catch block
		// e.printStackTrace();
		// }
		// ejecutarCliente();
		// Cliente aplicacion = new Cliente(port);
		// aplicacion.ejecutarCliente();
		
		smtp.info("Desconexion con servidor "+port);
		
	}

	// Hilo de lectura

	public Thread hiloLectura() {

		Thread hiloLectura = new Thread() {
			public void run() {

				while (conexionActiva) {
					//System.out.println("Hilo de lectura del cliente");
					try {
						//mensaje = entrada.readObject().toString();
						mensaje = entrada.readObject().toString();
						if (mensaje != null&&!modosilencioso) {
							System.out.println("RECIBIDO-"+port+"> " + mensaje);
						}

					}

					catch (ClassNotFoundException excepcionClaseNoEncontrada) {
						System.out
								.println("\nSe recibió un objeto de tipo desconocido");
					}

					catch (IOException e) {
						// TODO Auto-generated catch block
						// e.printStackTrace();
						System.out.println("El servidor " + port
								+ " cerro la conexion");
						// cerrarConexion(); //Comentarlo para Windows¿?
						conexionActiva = false;
					}

					// Inserción de en la base de datos

					if (mensaje != null) {
						//operacion.guardarEnBD(mensaje, port);
					}
					//
				}

				System.out.println("Fin hilo de lectura con servidor " + port);

			}
		};

		return hiloLectura;
	}

	// Hilo de escritura

	public Thread hiloEscritura() {

		Thread hiloEscritura = new Thread() {
			public void run() {

				while (conexionActiva) {

					// System.out.println("Hilo de escritura del cliente");
					
					// Establecer fecha si transcurre más de t2*60 minutos
					
					// (t2*60/t1) iteraciones del bucle para alcanzar las t2 horas
	

					if (contador > t2*60/t1 || contador == 0) {
						
						contador=1;
						stringSalida = establecerfecha + fecha() + '\n';

						if (salida != null && stringSalida != null) {

							try {
								salida.writeObject(stringSalida);
								salida.flush();
								if(!modosilencioso){
								System.out.println("ENVIADO-"+port+"> " + stringSalida);
								}
							}

							catch (IOException excepcionES) {
							}
						}
					}
					
					contador++;
	
					stringSalida = peticiontemperaturayhumedad; 


					if (salida != null && stringSalida != null) {

						try {
							salida.writeObject(stringSalida);
							//salida.writeBytes(stringSalida);
							salida.flush();
							if(!modosilencioso){
							System.out.println("ENVIADO-"+port+"> " + stringSalida);
							}
						}

						catch (IOException excepcionES) {
						}
					}

					try { // Espera entre la peticion de temperatura y la de
							// temperatura y humedad
						sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					stringSalida = peticiontemperatura;

					if (salida != null && stringSalida != null) {

						try {
							salida.writeObject(stringSalida);
							//salida.writeBytes(stringSalida);
							salida.flush();
							if(!modosilencioso){
							System.out.println("ENVIADO-"+port+"> " + stringSalida);
							}
						}

						catch (IOException excepcionES) {
						}
					}

					int j=1;
					while (j < 60*t1) {
						if (conexionActiva == true) {
							try { // Espera para volver a realizar peticiones
								sleep(1000);
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
						j++;
					}

				}

				System.out.println("Fin hilo de escritura con servidor " + port);
			}

		};

		return hiloEscritura;
	}

	// Hilo de lectura de comandos

	static public Thread hiloLecturaComandos() {

		Thread hiloLecturaComandos = new Thread() {

			public void run() {

				String cadenaEntrada = null;
				Scanner sc = new Scanner(System.in);
				
				modosilencioso=false;
				
				while (true) {
					//System.out.println("Comandos 1");
					try {
						sleep(200);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					cadenaEntrada = sc.next();// leerEntrada.readLine();
					//sc.
					System.out.println("Comando introducido: " + cadenaEntrada);

					//System.out.println("Comandos 2");

					if (cadenaEntrada.startsWith("desconectar")) {

						cadenaEntrada = sc.next();
						
						int i=0;
						boolean peticioncorrecta=false;
						boolean dirrecionincorrecta=false;
						boolean yadesconectado=false;
						int direccionleida=0;
						
						try{
						     direccionleida = Integer.parseInt(cadenaEntrada);
						}catch(NumberFormatException e){
						     //La cadena no se puede convertir a entero
							dirrecionincorrecta=true;							 
						} 
						
						while(i<numerodeservidores){
							
							if(clientes.elementAt(i).port==direccionleida){
								peticioncorrecta=true;
							}
							i++;
						}
						
						if(peticioncorrecta!=false&&dirrecionincorrecta!=true){
							//System.out.println(peticioncorrecta);
							//System.out.println(dirrecionincorrecta);
						if(clientes.elementAt(Integer.parseInt(cadenaEntrada) - base).cliente==null){
							
							System.out.println("Ya desconectado");
							yadesconectado=true;
						}
						}
						
						if(peticioncorrecta==true&&dirrecionincorrecta==false&&yadesconectado==false){
			

						System.out
								.println("Desconexion con el servidor "
										+ (Integer.parseInt(cadenaEntrada)));
						clientes.elementAt(Integer.parseInt(cadenaEntrada)
								- base).conexionActiva = false;

						try {
							sleep(5000);// Esperar que acaben los hilos de
										// lectura y escritura antes de cerrar
										// la conexion.
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

						clientes.elementAt(
								Integer.parseInt(cadenaEntrada) - base)
								.cerrarConexion();
						clientes.elementAt(Integer.parseInt(cadenaEntrada)
								- base).peticionDesconexion = true;
						
					}else{
						System.out.println("Peticion incorrecta");
					}


					}
					if (cadenaEntrada.startsWith("conectar")) {
						
						//System.out.println("---");
						cadenaEntrada = sc.next();
						//System.out.println("---"+cadenaEntrada);
												
						int i=0;
						boolean peticioncorrecta=false;
						boolean dirrecionincorrecta=false;
						boolean yaconectado=false;
						int direccionleida=0;
						
						try{
						     direccionleida = Integer.parseInt(cadenaEntrada);
						}catch(NumberFormatException e){
						     //La cadena no se puede convertir a entero
							dirrecionincorrecta=true;							 
						} 
						
						//System.out.println(peticioncorrecta);
						//System.out.println(dirrecionincorrecta);
						
						
						while(i<numerodeservidores){
							
							if(clientes.elementAt(i).port==direccionleida){
								peticioncorrecta=true;
								//System.out.println(clientes.elementAt(i).port);
								//System.out.println(direccionleida);
							}
							i++;
						}
						
						//System.out.println(peticioncorrecta);
						//System.out.println(dirrecionincorrecta);
						
						if(peticioncorrecta!=false&&dirrecionincorrecta!=true){
							//System.out.println(peticioncorrecta);
							//System.out.println(dirrecionincorrecta);
						if(clientes.elementAt(Integer.parseInt(cadenaEntrada) - base).cliente!=null){
							
							System.out.println("Ya conectado");
							yaconectado=true;
						}
						}
						
						
						if(peticioncorrecta==true&&dirrecionincorrecta==false&&yaconectado==false){


						//System.out.println(cadenaEntrada);
						if (Integer.parseInt(cadenaEntrada) == clientes
								.elementAt(Integer.parseInt(cadenaEntrada)
										- base).port) {
							clientes.elementAt(Integer.parseInt(cadenaEntrada)
									- base).peticionDesconexion = false;
						}
						
						}else{
							
							System.out.println("Peticion incorrecta");
						}


					}
					
					
					if (cadenaEntrada.startsWith("estado")) {
						
						//System.out.println("Comando introducido para mostrar servidores conectados");

						//System.out.println(cadenaEntrada);
						
						int i=0;
						while(i<numerodeservidores){
							
							System.out.print("Servidor "+Integer.toString(i+base)+": ");
							if(clientes.elementAt(i).cliente==null){
								System.out.println("conexion inactiva");
							}else{
								System.out.println("conexion activa. Modo test: "+clientes.elementAt(i).modotest);
							}
							i++;
						}

						//System.out.println("Comandos 6");

					}
					
					if (cadenaEntrada.startsWith("x")) {
						
						
						if(modosilencioso==false){
						
						System.out.println("No se mostrarán las tramas enviadas y recibidas");
						modosilencioso=true;
						
						//System.out.println("Modo silencioso: "+modosilencioso);
						
						}else if(modosilencioso==true){
						
						System.out.println("Se mostrarán las tramas enviadas y recibidas");
						modosilencioso=false;
						
						//System.out.println("Modo silencioso: "+modosilencioso);
						
						}
						
					}
					
					if (cadenaEntrada.startsWith("prueba")) {

						cadenaEntrada = sc.next();  //Lo siguiente debe ser la direccion IP
						
						int i=0;
						boolean peticioncorrecta=false;
						boolean dirrecionincorrecta=false;
						boolean servidordesconectado=false;
						int direccionleida=0;
						
						try{
						     direccionleida = Integer.parseInt(cadenaEntrada);
						}catch(NumberFormatException e){
						     //La cadena no se puede convertir a entero
							dirrecionincorrecta=true;							 
						} 
						
						while(i<numerodeservidores){
							
							if(clientes.elementAt(i).port==direccionleida){
								peticioncorrecta=true;
							}
							i++;
						}
						
						if(peticioncorrecta!=false&&dirrecionincorrecta!=true){
							//System.out.println(peticioncorrecta);
							//System.out.println(dirrecionincorrecta);
						if(clientes.elementAt(Integer.parseInt(cadenaEntrada) - base).cliente==null){
							
							System.out.println("El servidor está desconectado");
							servidordesconectado=true;
						}
						}
						
						if(peticioncorrecta==true&&dirrecionincorrecta==false&&servidordesconectado==false){
			
							if(clientes.elementAt(Integer.parseInt(cadenaEntrada)
									- base).modotest==false){

							clientes.elementAt(Integer.parseInt(cadenaEntrada)
									- base).operacion.CREADO="Prueba";	
							
							clientes.elementAt(Integer.parseInt(cadenaEntrada)
									- base).operacion.MODIFICADO="Prueba";	
							
							System.out.println("Empieza test con servidor "+ (Integer.parseInt(cadenaEntrada)));
							System.out.println("Los campos de la base de datos MODIFICADO y CREADO serán 'Prueba'");
							clientes.elementAt(Integer.parseInt(cadenaEntrada)
									- base).modotest=true;
							}else{
								System.out.println("Termina test con servidor "+ (Integer.parseInt(cadenaEntrada)));
								clientes.elementAt(Integer.parseInt(cadenaEntrada)
										- base).modotest=false;
								clientes.elementAt(Integer.parseInt(cadenaEntrada)
										- base).operacion.CREADO="Jesus";	
								
								clientes.elementAt(Integer.parseInt(cadenaEntrada)
										- base).operacion.MODIFICADO="Jesus";	
							}
						
					}else{
						System.out.println("Peticion incorrecta");
					}
						
			

					
					}	
					
					
					
					if (cadenaEntrada.startsWith("parametros")) {
						
						System.out.println("[Parametro (nombre): valor]");
						
						System.out.println("Base de datos: ");
						
						System.out.println("	URL: "+sURL);
						System.out.println("	user: "+user);
						System.out.println("	password: "+pass);
						
						System.out.println("Tiempo entre peticiones de temperatura y humedad en minutos (t1): "+t1);
						
						System.out.println("Tiempo entre peticiones de establecer fecha en horas (t2): "+t2);
						
						System.out.print("IPs (ip): ");
						
						int i=0;
						while(i<numerodeservidores){
							
							System.out.print(clientes.elementAt(i).port+" ");
								i++;
						}
						System.out.println("");
						
						System.out.println("Email alertas (e): "+email);

					}
					
					if (cadenaEntrada.startsWith("salir")) {
						
						System.out.println("Cerrando...");
						
						System.exit(-1);

					}
					
					if (cadenaEntrada.startsWith("?")) {
						
						System.out.println("Parámetros disponibles:");
						
						System.out.println("	desconectar ip: desconectar del servidor especificado por ip, si existe la conexión.");
						
						System.out.println("	conectar ip: conectar con el servidor especificado por ip, si hubo antes una desconexión. ");
						
						System.out.println("	estado: mostrar qué servidores están conectados y si está activo el modo prueba.");
						
						System.out.println("	prueba ip: iniciar una prueba con el servidor. Los datos se guardarán en la base de datos con el campo autor 'Test'.");
						
						System.out.println("	parametros: mostrar los parámetros del cliente: localización base de datos, lista de ips, tiempo entre peticiones de temperatura y humedad, tiempo entre peticiones de establecer fecha en los servidores, email para envío de alertas.");
						
						System.out.println("	salir: para salir del programa.");
						
						System.out.println("	x: para mostrar/no mostrar las tramas en la consola del cliente.");
						
						System.out.println("	configurar param valor: cambia el valor del parametro param.");
						
						System.out.println("	?: muestra los parámetros disponibles.");
						
					}
					
					
					if (cadenaEntrada.startsWith("configurar")) {
						
						//System.out.println("---");
						cadenaEntrada = sc.next();
						//System.out.println(cadenaEntrada);
						
						if(cadenaEntrada.startsWith("t1")){
							
							//System.out.println("---");		
							
							cadenaEntrada = sc.next();
							
							try{
							     t1 = Integer.parseInt(cadenaEntrada);
								 System.out.println("Nuevo valor de t1="+t1+" minutos");
							}catch(NumberFormatException e){
							     //La cadena no se puede convertir a entero
								System.out.println("Opción incorrecta");
															 
							} 
							
							
						}else if(cadenaEntrada.startsWith("t2")){
							
							cadenaEntrada = sc.next();
							
							try{
							     t2 = Integer.parseInt(cadenaEntrada);
								 System.out.println("Nuevo valor de t2="+t2+" horas");
							}catch(NumberFormatException e){
							     //La cadena no se puede convertir a entero
								System.out.println("Opción incorrecta");						 
							} 
							
						}else if(cadenaEntrada.startsWith("email")){
							
							email = sc.next();
							System.out.println("Nuevo email: "+email);
							
							
						}else{
							
							System.out.println("Opción incorrecta");
						}
						
						
					}
					

					//System.out.println("Comandos 7");
				}
				
				//System.out.println("Fin hilo de lectura de comandos");
			}


		};

		return hiloLecturaComandos;
	}

	static public Thread hiloEjecucionClientes(final int indice) {

		Thread hiloEjecucionClientes = new Thread() {

			public void run() {
				

				while (true) {

					//System.out.println("Comienzo HiloEjecucionClientes");
					clientes.elementAt(indice).ejecutarCliente();
					//System.out.println("Fin HiloEjecucionClientes");

					//System.out
						//	.println("Se reintentará conexion en 5 segundos.");
					//System.out
						//	.println(clientes.elementAt(indice).peticionDesconexion);

					// clientes.elementAt(indice).conexionActiva=true;

					try {
						sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					while (clientes.elementAt(indice).peticionDesconexion == true) {

						try {
							sleep(5000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						//System.out.println("Esperando peticion de conexion");

					}

					//System.out.println(indice);
					//System.out.println(base);
					clientes.setElementAt(new Cliente(indice + base), indice);
					// clientes.elementAt(indice).hiloLectura =
					// clientes.elementAt(indice).hiloLectura();
					// clientes.elementAt(indice).hiloEscritura
					// =clientes.elementAt(indice).hiloEscritura();
				}
				// Cliente aplicacion = new Cliente(port);
				// aplicacion.ejecutarCliente();

				// }

			}
		};

		return hiloEjecucionClientes;

	}

	// 
	public static String fecha() {

		String target_time_zone = "GMT-0:00";
		String format = "ss;mm;HH;dd;MM;yyyy";
		TimeZone tz = TimeZone.getTimeZone(target_time_zone);
		Date date = Calendar.getInstance().getTime();
		SimpleDateFormat date_format_gmt = new SimpleDateFormat(format);
		date_format_gmt.setTimeZone(tz);
		return date_format_gmt.format(date);
	}

	public static void main(String args[]) {

		int i = 0;

		//establecerfecha = "SET;7;";

		Thread hiloLecturaComandos = hiloLecturaComandos();
		hiloLecturaComandos.start();

		base = Integer.parseInt(args[0]);
		numerodeservidores= args.length;
		
        // Cargamos la configuración de Log4J desde un archivo accesible via CLASSPATH  

          
        // Obtenemos el Log para esta clase.  
        
        smtp.info("Iniciando programa");  

		PropertyConfigurator.configure("/home/jesus/workspace/Cliente/src/log4j.properties");
		//log.debug("Sample debug message");
		//log.info("Sample info message");
		//log.warn("Advertencia");
		//log.error("Sample error message");
		//log.fatal("Sample fatal message");
		
		
		while (i < args.length) {
			clientes.addElement(new Cliente(Integer.parseInt(args[i])));
			Thread hiloEjecucionClientes = hiloEjecucionClientes(i);
			hiloEjecucionClientes.start();
			i++;

		}

		// System.out.println("FIN BUCLE");
		// System.out.println("ESPERANDO HILO DE LECTURA DE COMANDOS");

	}

}
